// Traverse the entire dependency graph looking for -L paths to pass for
// native dependencies.
- cmd = push_native_dirs(cmd, &layout, package, cx, &mut HashSet::new());
+ let mut dirs = Vec::new();
+ each_dep(package, cx, |pkg| {
+ if pkg.get_manifest().get_build().len() > 0 {
+ dirs.push(layout.native(pkg));
+ }
+ });
+ for dir in dirs.into_iter() {
+ cmd = cmd.arg("-L").arg(dir);
+ }
for &(_, target) in cx.dep_targets(package).iter() {
cmd = try!(link_to(cmd, target, cx, kind, Dependency));
}
return Ok(cmd);
}
-
- fn push_native_dirs(mut cmd: ProcessBuilder, layout: &layout::LayoutProxy,
- pkg: &Package, cx: &Context,
- visited: &mut HashSet<PackageId>) -> ProcessBuilder {
- if !visited.insert(pkg.get_package_id().clone()) { return cmd }
-
- if pkg.get_manifest().get_build().len() > 0 {
- cmd = cmd.arg("-L").arg(layout.native(pkg));
- }
-
- match cx.resolve.deps(pkg.get_package_id()) {
- Some(mut pkgids) => {
- pkgids.fold(cmd, |cmd, dep_id| {
- let dep = cx.get_package(dep_id);
- push_native_dirs(cmd, layout, dep, cx, visited)
- })
- }
- None => cmd
- }
- }
}
pub fn process<T: ToCStr>(cmd: T, pkg: &Package, cx: &Context) -> ProcessBuilder {
// When invoking a tool, we need the *host* deps directory in the dynamic
// library search path for plugins and such which have dynamic dependencies.
+ let layout = cx.layout(KindPlugin);
let mut search_path = DynamicLibrary::search_path();
- search_path.push(cx.layout(KindPlugin).deps().clone());
- let search_path = os::join_paths(search_path.as_slice()).unwrap();
+ search_path.push(layout.deps().clone());
+
+ // Also be sure to pick up any native build directories required by plugins
+ // or their dependencies
+ let mut native_search_paths = HashSet::new();
+ for &(dep, target) in cx.dep_targets(pkg).iter() {
+ if !target.get_profile().is_plugin() { continue }
+ each_dep(dep, cx, |dep| {
+ if dep.get_manifest().get_build().len() > 0 {
+ native_search_paths.insert(layout.native(dep));
+ }
+ });
+ }
+ search_path.extend(native_search_paths.into_iter());
// We want to use the same environment and such as normal processes, but we
// want to override the dylib search path with the one we just calculated.
+ let search_path = os::join_paths(search_path.as_slice()).unwrap();
cx.compilation.process(cmd, pkg)
.env(DynamicLibrary::envvar(), Some(search_path.as_slice()))
}
+
+fn each_dep<'a>(pkg: &Package, cx: &'a Context, f: |&'a Package|) {
+ let mut visited = HashSet::new();
+ let pkg = cx.get_package(pkg.get_package_id());
+ visit_deps(pkg, cx, &mut visited, f);
+
+ fn visit_deps<'a>(pkg: &'a Package, cx: &'a Context,
+ visited: &mut HashSet<&'a PackageId>,
+ f: |&'a Package|) {
+ if !visited.insert(pkg.get_package_id()) { return }
+ f(pkg);
+ let mut deps = match cx.resolve.deps(pkg.get_package_id()) {
+ Some(deps) => deps,
+ None => return,
+ };
+ for dep_id in deps {
+ visit_deps(cx.get_package(dep_id), cx, visited, |p| f(p))
+ }
+ }
+}
+use std::io::fs;
+use std::os;
+
use support::{project, execs, cargo_dir};
use hamcrest::assert_that;
assert_that(foo.process(cargo_dir().join("cargo")).arg("doc"),
execs().with_status(0));
})
+
+test!(plugin_with_dynamic_native_dependency {
+ let build = project("build")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "build"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "build"
+ crate-type = ["dylib"]
+ "#)
+ .file("src/main.rs", r#"
+ use std::io::fs;
+ use std::os;
+
+ fn main() {
+ let src = Path::new(os::getenv("SRC").unwrap());
+ let dst = Path::new(os::getenv("OUT_DIR").unwrap());
+ let dst = dst.join(src.filename().unwrap());
+ fs::rename(&src, &dst).unwrap();
+ }
+ "#)
+ .file("src/lib.rs", r#"
+ #[no_mangle]
+ pub extern fn foo() {}
+ "#);
+ assert_that(build.cargo_process("build"),
+ execs().with_status(0).with_stderr(""));
+ let src = build.root().join("target");
+ let lib = fs::readdir(&src).unwrap().into_iter().find(|lib| {
+ let lib = lib.filename_str().unwrap();
+ lib.starts_with(os::consts::DLL_PREFIX) &&
+ lib.ends_with(os::consts::DLL_SUFFIX)
+ }).unwrap();
+ let libname = lib.filename_str().unwrap();
+ let libname = libname.slice(os::consts::DLL_PREFIX.len(),
+ libname.len() - os::consts::DLL_SUFFIX.len());
+
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/main.rs", r#"
+ #![feature(phase)]
+ #[phase(plugin)] extern crate bar;
+
+ fn main() {}
+ "#)
+ .file("bar/Cargo.toml", format!(r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+ build = '{}'
+
+ [lib]
+ name = "bar"
+ plugin = true
+ "#, build.bin("build").display()))
+ .file("bar/src/lib.rs", format!(r#"
+ #![feature(plugin_registrar)]
+
+ extern crate rustc;
+
+ use rustc::plugin::Registry;
+
+ #[link(name = "{}")]
+ extern {{ fn foo(); }}
+
+ #[plugin_registrar]
+ pub fn bar(_reg: &mut Registry) {{
+ unsafe {{ foo() }}
+ }}
+ "#, libname));
+
+ assert_that(foo.cargo_process("build").env("SRC", Some(lib.as_vec())),
+ execs().with_status(0));
+})